package task1.core;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import task1.parsers.Bean;

public class XmlBeanFactory implements BeanFactory {
	
	HashMap<String, Object> beanTable = new HashMap<String, Object>();
	
	XmlBeanFactory(String xmlFilePath, XmlBeanDefinitionReader xbdr) {		
		xbdr.loadBeanDefinitions(xmlFilePath);
		//System.out.println(xbdr.getBeanList().toString());
		generateBeans(xbdr.getBeanList());
	}
	
	private void generateBeans(List<Bean> beanList) {
				
		for (Bean b : beanList) {
			try {
				final Class<?> clazz = Class.forName(b.getClassName());
				Constructor<?> ctor;
				Object object;
				
				List<String> ca = b.getConstructorArg();
				
				if (!ca.isEmpty()) {
					Class[] consClasses = new Class[ca.size() / 2];
					for (int i = 0, j = 0; i < ca.size(); i+=2) {
						if (ca.get(i) == null) {
							consClasses[j] = String.class;
						} else if (ca.get(i).contentEquals("int")) { 
							consClasses[j] = int.class;
						} else if (ca.get(i).contentEquals("String")){
							consClasses[j] = String.class;
						} else {
							consClasses[j] = Class.forName(ca.get(i));
						}
						j++;
					}
					ctor = clazz.getConstructor(consClasses);
					Object[] consArgs = new Object[consClasses.length];
					for (int i = 1, j = 0; i < ca.size(); i+=2) {
						if (consClasses[j] == int.class){
							consArgs[j] = Integer.valueOf(ca.get(i));
						}
						else {
							consArgs[j] = consClasses[j].cast(ca.get(i));
						}
						j++;
					}
					object = ctor.newInstance(consArgs);
				} else {
					ctor = clazz.getConstructor();
					object = ctor.newInstance();
				}
				
				List<String> props = b.getProperties();
				
				if (!props.isEmpty()) {
					for (int i = 0; i < props.size(); i++) {
						char first = Character.toUpperCase(props.get(i).charAt(0));
						String methodName = "set" + first + props.get(i).substring(1);
						Method method = object.getClass().getMethod(methodName, new Class[] { props.get(i+1).getClass() });               
						method.invoke(object, props.get(i+1));
						i++;
					}
				}

				beanTable.put(b.getName(), object);
			}
			catch(Exception ex) {
				ex.printStackTrace();
			}
		}
	}

	public Object getBean(String string) {
		return beanTable.get(string);
	}

	public <T> T getBean(String string, Class<T> type) {
		return (T)beanTable.get(string);
	}

}
